package org.dei.perla.sys.device.channel.frame.receiver;

import org.dei.perla.sys.device.channel.frame.FrameConfiguration;
import org.dei.perla.sys.device.channel.frame.header.FrameHeader;
import org.dei.perla.sys.device.channel.frame.header.FrameHeaderReceiver;
import org.dei.perla.sys.device.channel.frame.header.FrameHeaderReceiverEvents;

/**
 * Ricevitore di un pacchetto headerful
 *
 */
public class FrameReceiverHeaderFul extends FrameReceiver {

	/**
	 * Stati del ricevitore headerful
	 *
	 */
	private enum State {
		
		// Stato di attesa del byte delimitatore di inizio frame
		Start,			
		
		// Stato di ricezione payload
		ReceivingPayload, 
		
		// Stato di riconoscimento di una sequenza di escape 
		Escaping,
		
		// Stato di ricezione header
		ReceivingHeader,
		
		// Stato di riconoscimento di una sequenza di escape nell'header
		EscapingHeader
	};
	
	/**
	 * Oggetto per riconoscimento di un header di un frame in uno stream di byte 
	 */
	private FrameHeaderReceiver pHeaderReceiver;
	
	/**
	 * Gestore eventi di ricezione header
	 */
	private FrameHeaderReceiverEvents pHeaderReceiverEvents;
	
	/**
	 * Ultimo header valido ricevuto
	 */
	private FrameHeader pLastFrameHeader;
	
	/**
	 * Stato corrente del ricevitore
	 */
	private State pState;
	
	/**
	 * Inizializza il ricevitore di frame headerful
	 * @param parConfiguration Configurazione frame
	 * @param parFrameHeaderReceiver Ricevitore header
	 */
	public FrameReceiverHeaderFul(FrameConfiguration parConfiguration, FrameHeaderReceiver parFrameHeaderReceiver) {
		
		super(parConfiguration);
		
		// Inizializza lo stato del ricevitore
		this.pState = State.Start;
		
		// Crea un gestore di eventi di ricezione di header
		this.pHeaderReceiverEvents = new FrameHeaderReceiverEvents () {

			public void onError(String parErrorMessage) {
				pEvents.onError(parErrorMessage);
			}

			public void onFrameHeaderReceived(FrameHeader parFrameHeader) {
				
				pState 				= State.ReceivingPayload;
				pLastFrameHeader 	= parFrameHeader;
				
			}
			
			
		};
		
		// Imposta il ricevitore degli header e associa il gestore di eventi relativo
		this.pHeaderReceiver = parFrameHeaderReceiver;
		this.pHeaderReceiver.start(this.pHeaderReceiverEvents);
	}
	
	@Override
	protected void pNotifyFrame() {

		byte[] tBuffer;

		// Copia il frame ricevuto in un nuovo vettore 
		if (this.pBufferCount > 0)
			tBuffer = java.util.Arrays.copyOfRange(this.pBuffer, 0,
					this.pBufferCount);
		else
			tBuffer = new byte[0];

		// Clona l'ultimo header
		FrameHeader tFrameHeader = pLastFrameHeader.clone();

		// Invia l'evento
		this.pEvents.onFrameReceivedHeaderFul(tBuffer, tFrameHeader);
	}
	
	@Override
	public void reset() {
		this.pState = State.Start;
		this.pBufferReset();
	}
	
	@Override
	public void receiveByte(byte parReceivedByte) {
		
		// Ricevo il payload
		if (this.pState == State.ReceivingPayload) {

			// Ricevuto byte di escape
			if (parReceivedByte == this.pConfiguration.getByteEscape()) {
				this.pState = State.Escaping;
			}
			
			// Ricevuto terminatore
			else if (parReceivedByte == this.pConfiguration.getByteDelimiterEnd()) {
				this.pNotifyFrame();
				this.reset();
			}
			
			// Ricevuto un delimitatore d'inizio frame
			else if (parReceivedByte == this.pConfiguration.getByteDelimiterStartHeaderFul()) {
				this.pNotifyUnclosedFrame();
				this.reset();
			}
			
			// Ricevuto un byte valido
			else {
				this.pBufferAppend(parReceivedByte);
			}
			
		}
		
		// Ricevo un byte con escape
		else if (this.pState == State.Escaping) {
			
			this.pBufferAppend(parReceivedByte);
			this.pState = State.ReceivingPayload;
			
		}
			
		// Ricevo l'header del frame
		else if (this.pState == State.ReceivingHeader) {
			
			// Ricevo un byte di escape
			if (parReceivedByte == this.pConfiguration.getByteEscape()) {
				this.pState = State.EscapingHeader;
			}
			
			// Ricevuto un delimitatore d'inizio frame
			else if (parReceivedByte == this.pConfiguration.getByteDelimiterStartHeaderFul()) {
				this.pNotifyUnclosedFrame();
				this.reset();
			}
				
			// Ricevuto un terminatore
			else if (parReceivedByte == this.pConfiguration.getByteDelimiterStartHeaderFul()) {
				this.pNotifyInvalidByte(parReceivedByte);
				this.reset();
			}
				
			// Ricevo un byte valido
			else {
				this.pHeaderReceiver.receiveByte(parReceivedByte);
			}
			
		}
			
		// Ricevo un byte con escape durante la ricezione dell'header
		else if (this.pState == State.EscapingHeader) {
			
			this.pHeaderReceiver.receiveByte(parReceivedByte);
			this.pState = State.ReceivingPayload;
			
		}
		
		// Ricezione delimitatore inizio frame
		else {

			// Ricevuto un delimitatore d'inizio frame
			if (parReceivedByte == this.pConfiguration.getByteDelimiterStartHeaderFul()) {
				this.pState = State.ReceivingHeader;
			}

			// Riceuto altro
			else {
				this.pNotifyInvalidByte(parReceivedByte);
				this.reset();
			}

		}	
	
	}

}
